package toolib.geom;

/**
 * Line by two vectors.
 * 
 * @author Steffen Fiedler
 */
public class Line2D {
	
	/**
	 * Line point <code>a</code>.
	 */
	public V2f a;
	
	/**
	 * Line point <code>b</code>.
	 */
	public V2f b;
	
	public static float onLineVariation	= 0.005f;
	
	/**
	 * Creates a new line between <code>a</code>(0,0) 
	 * and <code>b</code>(0,0).
	 */
	public Line2D () {
		a = new V2f ();
		b = new V2f ();
	}
	
	/**
	 * Creates a new line between <code>a</code>(0,0) and 
	 * a parametric specified point <code>b</code>.
	 * 
	 * @param theBX Point B x-coordinate.
	 * @param theBY Point B y-coordinate.
	 */
	public Line2D (float theBX, float theBY) {
		a = new V2f ();
		b = new V2f (theBX, theBY);
	}
	
	/**
	 * Creates a new line between the parametric 
	 * specified points <code>a</code> and <code>b
	 * </code>.
	 * 
	 * @param theAX Point A x-coordinate.
	 * @param theAY Point A y-coordinate.
	 * @param theBX Point B x-coordinate.
	 * @param theBY Point B y-coordinate.
	 */
	public Line2D (float theAX, float theAY, float theBX, float theBY) {
		a = new V2f (theAX, theAY);
		b = new V2f (theBX, theBY);
	}
	
	/**
	 * Creates a new line between point <code>
	 * a</code>(0,0) and parametric specified 
	 * vector <code>b</code>.
	 * 
	 * @param theB Point B.
	 */
	public Line2D (V2f theB) {
		a = new V2f ();
		b = theB;
	}
	
	/**
	 * Creates a new line between the parametric 
	 * specified vectors <code>a</code> and 
	 * <code>b</code>.
	 * 
	 * @param theA Point A.
	 * @param theB Point B.
	 */
	public Line2D (V2f theA, V2f theB) {
		a = theA;
		b = theB;
	}
	
	/**
	 * Shortest distance of a given point to the line.
	 * 
	 * @param theX Point x-coordinate.
	 * @param theY Point y-coordinate.
	 * 
	 * @return Length of the way between the given 
	 * point and the line.
	 */
	public final float distance (float theX, float theY) {
		if (a.x == b.x) {
			return Math.abs (theX - a.x);
		}else if (a.y == b.y) {
			return Math.abs (theY - a.y);
		}else{
			float m1 = slope ();
			float b1 = a.y - (m1 * a.x);
			float m2 = 1 /-m1;
			float b2 = theY - (m2 * theX);
			
			float x = (b2 - b1) / (m1 - m2);
			
			return (float) Math.sqrt (Math.pow (theX - x, 2) + Math.pow (theY - (m1 * x) + b1, 2));
		}
	}
	
	/**
	 * Shortest distance of a given vector to the line.
	 * 
	 * @param theVector Vector describing the point.
	 * @return Length of the way between the vector 
	 * an the line.
	 */
	public final float distance (V2f theVector) {
		return distance (theVector.x, theVector.y);
	}
	
	public final float distance (Line2D theLine) {
		return 0;
	}
	
	/**
	 * Distance between point A and point B.
	 * 
	 * @return Length of the line.
	 */
	public final float length () {
		return (float) Math.sqrt (Math.pow (a.x - b.x, 2) + Math.pow (a.y - b.y, 2));
	}
	
	/**
	 * Method verifies if a point (by x and y coordinate) 
	 * is places on the line.
	 * 
	 * @param theX Point x-coordinate.
	 * @param theY Point y-coordinate.
	 * 
	 * @return Returns <code>true</code> if the given 
	 * point lies on the line; otherwise <code>false</code>.
	 */
	public final boolean hasPoint (float theX, float theY) {
		return (a.distance (theX, theY) + b.distance (theX, theY) - length () < onLineVariation) ? true : false;
	}
	
	/**
	 * Method verifies if a vector is placed on the line.
	 * 
 	 * @param thePoint Point to look for.
 	 * 
	 * @return Returns <code>true</code> if the given
	 * point lies on the line; otherwise <code>false</code>.
	 */
	public final boolean hasPoint (V2f thePoint) {
		return hasPoint (thePoint.x, thePoint.y);
	}
	
	/**
	 * Method reviews if the line is parallel to another 
	 * given line.
	 * 
	 * @param theLine Line to check with.
	 * 
	 * @return Returns <code>true</code> if both lines have 
	 * the same slope and both rays have no intersection; 
	 * otherwise <code>false</code> will be the result.
	 */
	public final boolean isParallel (Line2D theLine) {
		return slope () == theLine.slope () ?  true : false;
	}
	
	public final V2f normalVector () {
		return new V2f (b.y - a.y, - (b.x - a.x));
	}
	
	/**
	 * Part of the line segment from point <code>a</code> 
	 * to point <code>b</code>. Starting from position 
	 * <code>a</code> the section has the percentage length 
	 * of the whole distance between <code>a</code> and 
	 * <code>b</code>.
	 * 
	 * @param theProgress Percentage length of the section 
	 * between 0 and 1 - where 1 is the complete segment and 
	 * 0 a line from <code>a</code> to <code>a</code>.
	 * 
	 * @return Section of the line as new line. Point <code>
	 * a</code> will be returned as a copy. Use <code>equals
	 * </code> to compare it with the original.
	 */
	public final Line2D section (float theProgress) {
		return new Line2D (a.clone (), V2f.sum (a, V2f.sub (b, a).scale (theProgress)));
	}
	
	/**
	 * Part of the line segment from point <code>a</code> 
	 * to point <code>b</code>. Both points of the new line 
	 * will be defined relative to point <code>a</code> of 
	 * the original line.
	 * 
	 * @param theStart Section start point on the line from 
	 * point a, relative to the length of the line.
	 * @param theEnd Section end point on the line from point 
	 * a, relative to the length of the line.
	 * 
	 * @return Section of the line as new line instance. Point 
	 * <code>a</code> will be returned as a copy. Use <code>
	 * equals</code> to compare it with the original.
	 */
	public final Line2D section (float theStart, float theEnd) {
		return new Line2D (	V2f.sum (a, V2f.sub (b, a).scale (theStart)), 
							V2f.sum (a, V2f.sub (b, a).scale (theEnd)));
	}
	
	/**
	 * Redefines the coordinates of both points which define 
	 * the line at once.
	 * 
	 * @param theAX Point A x-coordinate.
	 * @param theAY Point A y-coordinate.
	 * @param theBX Point B x-coordinate.
	 * @param theBY Point B y-coordinate.
	 */
	public final void set (float theAX, float theAY, float theBX, float theBY) {
		a.x = theAX;
		a.y = theAY;
		b.x = theAX;
		b.y = theAY;
	}
	
	/**
	 * Redefines the coordinates of both points which define 
	 * the line a once.
	 * 
	 * @param theA Point A.
	 * @param theB Point B.
	 */
	public final void set (V2f theA, V2f theB) {
		a = theA;
		b = theB;
	}
	
	/**
	 * Slope (steepness) of the line between point <code>
	 * a</code> and point <code>b</code> - as deltaX / deltaY.
	 * 
	 * @return 
	 */
	public final float slope () {
		return (a.y - b.y) / (a.x - b.x);
	}
	
	public final V2f vector () {
		return new V2f (b.x - a.x, b.y - a.y);
	}
	
	/**
	 * Copy of the line.
	 * 
	 * @return Full copy of the line instance.
	 */
	public final Line2D clone () {
		return new Line2D (	a.x, 
							a.y, 
							b.x, 
							b.y);
	}
}